import java.util.*;
import java.text.*;

/**
 *	@author Eric
 *	Date: 		4/14/13
 *	Completed Date:	4/26/13
 *
 *	Calculates the angles of the hour line on a sundial from the gnomon.
 *	The hour lines go from 6am to 6 pm.
 */

public class sundialCalculation {
	
	double latitude, longitude;
	Calendar date;	
	ArrayList<Double> hourAngles;
	ArrayList<Double> adjustedHourAngles;

	/**
	 *
	 * @param latitude
	 * @param longitude
	 * @param date
	 *
	 */
	public sundialCalculation (double latitude, double longitude, Calendar date) {
		this.latitude = Math.abs(latitude);
		this.longitude = longitude;
		this.date = date;
	}
	
	/**
	 *
	 * Calculates the angles of the hour lines using the user's latitude.
	 * The angles are in degrees away from the gnomon.  A negative angle means
	 * that the line is on the left of the gnomon.
	 *
	 */
	public void calculateHourAngles (){
		hourAngles = new ArrayList<Double> ();
		double hourAngle= 0;
		hourAngles = new ArrayList<Double> ();
		for (int i = -6; i < 7; i++) {
			hourAngle = Math.atan(Math.sin(Math.toRadians(latitude))*Math.tan(Math.toRadians(15*i)));
			hourAngle = Math.toDegrees(hourAngle);
			hourAngles.add(hourAngle);
		}
	}

	/**
	 *
	 * Calculates the angles of the hour lines after factoring in the time zone and eot
	 * corrections.  The angles are stored as degrees in the adjustedHourAngles arraylist.
	 *
	 */
	public void calculateAdjustedHourAngles () {
		adjustedHourAngles = new ArrayList<Double> ();
		double newHourAngle = 0;
		double longitudeCorrection = longitudeCorrection();
		double eotCorrection = EOT();
		for (int i = 0; i < hourAngles.size(); i++) {
			newHourAngle = hourAngles.get(i) + longitudeCorrection + eotCorrection;
			adjustedHourAngles.add(newHourAngle);
		}
	}

	/**
	 *
	 * @return the arraylist that contains all of the adjusted angles of the hour lines from the gnomon
	 *
	 */
	public ArrayList<Double> getAdjustedHourAngles () {
		calculateHourAngles ();
		calculateAdjustedHourAngles ();
		return adjustedHourAngles;
	}	
	
	/**
	 *
	 * Calculates the difference from the Standard Meridian and longitude.
	 * 
	 * @return The difference in degrees that applied to the angles of the hour line.
	 *
	 */
	public double longitudeCorrection () {
		double meridianLongitude = 0;
		double timeZoneLongitude = 0;
		double longitudeCorrection = 0;
		meridianLongitude = longitude%15;
		if (longitude >= 0) {
			//West of the Meridian on East Hemisphere
			if (meridianLongitude > 7.5 && meridianLongitude <=15) {
				timeZoneLongitude = Math.ceil(longitude/15)*15;
			//East of the Meridian on East Hemisphere
			} else if (meridianLongitude >=0 && meridianLongitude <=7.5) {
				timeZoneLongitude = Math.floor(longitude/15)*15;
			}
			longitudeCorrection = longitude - timeZoneLongitude;
		} else if (longitude < 0) {
			//West of the Meridian on West Hemisphere
			if (meridianLongitude > -7.5 && meridianLongitude <=0) {
				timeZoneLongitude = Math.ceil(longitude/15)*15;
			//East of the Meridian on West Hemisphere
			} else if (meridianLongitude >=-15 && meridianLongitude <=-7.5) {
				timeZoneLongitude = Math.floor(longitude/15)*15;
			}
			longitudeCorrection = Math.abs(timeZoneLongitude) - Math.abs(longitude);
		}
		return longitudeCorrection;
	}

	/**
	 *
	 * Calculates the EOT from the day of the year and using the formula that 
	 * Brent provided to us.  The EOT is in minutes and is convert to degrees.
	 *
	 * @return the EOT that is needed to adjust our sundial in degrees.
	 *
	 */
	public double EOT () {
		int dayNumber = date.get(Calendar.DAY_OF_YEAR);
		double eot = 0;
		double b = 360.0*(dayNumber - 81)/365.0;
		b = Math.toRadians(b);
		eot = 9.87*Math.sin(2*b)-7.53*Math.cos(b)-1.5*Math.sin(b);
		//EOT in minutes.  There are 4 minutes in a degree.
		eot = eot/4.0;
		return eot;
	}
}